package valuecomusa.fijibus.eticket.service.shorthaul;

import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.*;
import android.util.Log;
import android.widget.Toast;
import valuecomusa.fijibus.eticket.DataCenter;
import valuecomusa.fijibus.eticket.Utilities;
import valuecomusa.fijibus.eticket.db.shorthaul.BusinessDBHandler;
import valuecomusa.fijibus.eticket.dto.DataDeleted;
import valuecomusa.fijibus.eticket.dto.SyncStatusValue;

import java.util.*;

public class DataSyncService extends Service
{
    private final String TAG = "DataSyncSvc-Short";
    private Timer AutoLoadTimer = new Timer();
    private boolean isUpdating = false;
    private final int SYNC_INTERVAL = 25000; //25s

    private UIThreadHandler uiThreadHandler = null;

	@Override
	public IBinder onBind(Intent intent)
	{
		return null;
	}

	@Override
	public void onCreate()
	{
        //Log.d(TAG, "onCreate...");
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		// We want this service to continue running until it is explicitly
		// stopped, so return sticky.
		//Log.d(TAG, "[onStartCommand] Starting...");

        HandlerThread uiThread = new HandlerThread("UIThreadHandler");//to prepare a looper
        uiThread.start();
        uiThreadHandler = new UIThreadHandler(uiThread.getLooper()); //to process UI such as AlertDialog,...

		AutoLoadTimer.schedule(
                task,
                5000, //first run after 5s
                SYNC_INTERVAL); //next run each 25s after first run
		return START_STICKY;
	}

	@Override
	public void onDestroy()
	{
		// Cancel the persistent notification.
		//Log.d(TAG, "Stopping...");
		AutoLoadTimer.cancel();
	}

	TimerTask task = new TimerTask()
	{

		@Override
		public void run()
		{
			if (!isUpdating)
			{
                isUpdating = true;
				doDataSync();
                isUpdating = false;
			}
            //else
                //Log.d(TAG, "Another task is running...");

		}
	};

    private void WriteSyncStatus(String content, boolean showNotify)
    {
		try
		{
			SyncStatusValue value = new SyncStatusValue();
			value.ISN = 1;
			value.Status = content;
			//--
			Calendar cal = Calendar.getInstance();
			cal.add(Calendar.MILLISECOND, SYNC_INTERVAL);
			Date nextSyn = cal.getTime();
			//--
			value.NextSync = Utilities.DateToString(nextSyn, "MM/dd/yyyy hh:mm:ss a");
			value.LastUpdate = Utilities.DateToString(new Date(), "MM/dd/yyyy hh:mm:ss a");
			//-------
			BusinessDBHandler db = new BusinessDBHandler(getApplicationContext());
			db.addOrUpdateSyncStatus(value);
			//--
			db.addOrUpdateRuntimeValues("lastupdate",
					String.valueOf(Calendar.getInstance().getTimeInMillis()));

			if(showNotify)
			{
				if(!content.equals("Successful"))
					notifySyncStatus("Data sync error: " + content);
				else
					notifySyncStatus("Data sync " + content);
			}
		}
		catch (Exception ex)
		{
			//Log.d(TAG, "[WriteSyncStatus] Exception: " + ex.getMessage());
		}
    }

    private void notifySyncStatus(String content)
    {
        Message msg = new Message();
        msg.what = UIThreadHandler.MSG_SYNC_STATUS;
        Bundle data = new Bundle();
        data.putString("SYNC_STATUS_CONTENT", content);
        msg.setData(data);
        uiThreadHandler.sendMessage(msg);
    }

    private int runCount = 0;
	private void doDataSync()
	{
        try
        {
            BusinessDBHandler db = new BusinessDBHandler(getApplicationContext());
            String xLoaded = db.getRuntimeValues("loaded");
            String xLastUpdate = db.getRuntimeValues("lastupdate");
            if(xLoaded == null || xLoaded.length() == 0)
            {
                //Log.d(TAG, "[doDataSync] Application could not loaded yet.");
                return;
            }

            if(xLastUpdate == null || xLastUpdate.length() == 0)
            {
                //Log.d(TAG, "Could not get last update. Set to NOW");
                xLastUpdate = String.valueOf(Calendar.getInstance().getTimeInMillis());
            }

            if (!isNetworkAvailable())
            {
                //Log.d(TAG, "[doDataSync] Network not available.");
                WriteSyncStatus("Network not available", true);
                uiThreadHandler.sendEmptyMessage(UIThreadHandler.MSG_ERROR_NO_NETWORK);
                return;
            }
            //---
            runCount++;
            //--
            int updateSuccess = 0;
            //--
            //Log.d(TAG, "[doDataSync] Last update: " + xLastUpdate);
            DataCenter dataCenter = new DataCenter(getApplicationContext());
            //Log.d(TAG, "[doDataSync] Sync transaction to server...");
            updateSuccess |= dataCenter.UpdateTransaction()?1:0;
            //Log.d(TAG, "[doDataSync] Sync journal to server...");
            updateSuccess |= dataCenter.UpdateJournal()?1:0;
            //--
            if(runCount >= 4) //1 mins
            {
                runCount = 0;

                int xCount = GetUpdateDataDeleted();
                updateSuccess |= xCount>0?1:0;
                //--
                xCount = GetMaterialDataUpdate(xLastUpdate);
                updateSuccess |= xCount>0?1:0;
                //--
                xCount = GetUpdateRecording(db);
                updateSuccess |= xCount>0?1:0;
                //--
                //Log.d(TAG, "[doDataSync] Get default Card Number...");
                String x = dataCenter.getDefaultCardNumber();
                if(x == null || x.length() == 0)
                {
                    //Log.d(TAG, "[doDataSync] Get default Card Number failed");
                    //WriteSyncStatus("Failed to get default customer info.");
                }
                /*else
                    WriteSyncStatus("Successful");*/
            }
            /*else
                WriteSyncStatus("Successful");*/
            //--
            WriteSyncStatus("Successful", updateSuccess > 0);
            //--
            //Log.d(TAG, "[doDataSync] DONE");
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
            WriteSyncStatus("Error. " + ex.getMessage(), true);
		}
	}

    private int GetMaterialDataUpdate(String xLastUpdate)
    {
        //Log.d(TAG, "[GetMaterialDataUpdate] Check MATERIAL data update...");
        DataCenter dataCenter = new DataCenter(getApplicationContext());
        int xCount = 0;
        try
        {
            xCount = dataCenter.checkDataUpdate(xLastUpdate);

            //Log.d(TAG, "[GetMaterialDataUpdate] " + xCount);
            if(xCount > 0)
            {
                int x = 0;
                //region get RAW data
                ///--- 1
                //Log.d(TAG, "[GetMaterialDataUpdate] Conductor");
                x = dataCenter.GetConductorData(null, x, xLastUpdate);
                //--- 2
                //Log.d(TAG, "[GetMaterialDataUpdate] Member");
                x = dataCenter.GetMemberData(null, x, xLastUpdate);
                //--- 3
                //Log.d(TAG, "[GetMaterialDataUpdate] Bus");
                x = dataCenter.GetBusData(null, x, xLastUpdate);
                //--- 4
                //Log.d(TAG, "[GetMaterialDataUpdate] Route");
                x = dataCenter.GetRouteData(null, x, xLastUpdate);
                //--- 5
                //Log.d(TAG, "[GetMaterialDataUpdate] Route_Bus");
                x = dataCenter.GetRouteAssignData(null, x, xLastUpdate);
                //--- 6
                //Log.d(TAG, "[GetMaterialDataUpdate] Stage");
                x = dataCenter.GetStageData(null, x, xLastUpdate);
                //--- 7
                //Log.d(TAG, "[GetMaterialDataUpdate] BusStation");
                x = dataCenter.GetBusStationData(null, x, xLastUpdate);
                //--- 8
                //Log.d(TAG, "[GetMaterialDataUpdate] BusStop");
                x = dataCenter.GetBusStopData(null, x, xLastUpdate);
                //--- 9
                //Log.d(TAG, "[GetMaterialDataUpdate] ExpressFare");
                x = dataCenter.GetExpressFareData(null, x, xLastUpdate);
                //--- 10
                //Log.d(TAG, "[GetMaterialDataUpdate] StageFare");
                x = dataCenter.GetStageFareData(null, x, xLastUpdate);
                //--- 11
                //Log.d(TAG, "[GetMaterialDataUpdate] BusSeat");
                x = dataCenter.GetBusSeatData(null, x, xLastUpdate);
                //--- 12
                //Log.d(TAG, "[GetMaterialDataUpdate] BusAssignment");
                x = dataCenter.GetBusAssignData(null, x, xLastUpdate);
                //--- 13
                //Log.d(TAG, "[GetMaterialDataUpdate] PassengerType");
                x = dataCenter.GetPassengerTypeData(null, x, xLastUpdate);
                //endregion
                //Log.d(TAG, "[GetMaterialDataUpdate] Update total: " + x);
            }
            else
            {
                //Log.d(TAG, "[GetMaterialDataUpdate] No RAW data for update");
            }
        }
        catch (Exception ex)
        {
            //Log.d(TAG, "[GetMaterialDataUpdate] EXCEPTION: " + ex);
        }

        return xCount;
    }

    private int GetUpdateRecording(BusinessDBHandler db)
    {
        DataCenter dataCenter = new DataCenter(getApplicationContext());
        int xCount = 0;

        try
        {
            //Log.d(TAG, "[GetUpdateRecording] bula.mp3...");
            //--//bula.mp3, insufficient-balance.mp3, negative-balance.mp3, tapout.mp3
            String checkSum = db.getRuntimeValues("bula.mp3");
            if(!Utilities.FileExistOnSdCard("bula.mp3"))
                checkSum = "";
            boolean bulaChanged = dataCenter.checkRecordingFileChanged(1, checkSum);
            xCount += bulaChanged == true?1:0;
            //-
            //Log.d(TAG, "[GetUpdateRecording] insufficient-balance.mp3...");
            checkSum = db.getRuntimeValues("insufficient-balance.mp3");
            if(!Utilities.FileExistOnSdCard("insufficient-balance.mp3"))
                checkSum = "";
            boolean insufficientChanged = dataCenter.checkRecordingFileChanged(2, checkSum);
            xCount += insufficientChanged == true?1:0;
            //-
            //Log.d(TAG, "[GetUpdateRecording] negative-balance.mp3...");
            checkSum = db.getRuntimeValues("negative-balance.mp3");
            if(!Utilities.FileExistOnSdCard("negative-balance.mp3"))
                checkSum = "";
            boolean negativeChanged = dataCenter.checkRecordingFileChanged(3, checkSum);
            xCount += negativeChanged == true?1:0;
            //-
            //Log.d(TAG, "[GetUpdateRecording] tapout.mp3...");
            checkSum = db.getRuntimeValues("tapout.mp3");
            if(!Utilities.FileExistOnSdCard("tapout.mp3"))
                checkSum = "";
            boolean tapoutChanged = dataCenter.checkRecordingFileChanged(4, checkSum);
            xCount += tapoutChanged == true?1:0;

            if(!Utilities.FileExistOnSdCard("process-complete.mp3"))
                checkSum= "";
            boolean checkinComplete = dataCenter.checkRecordingFileChanged(5,checkSum);
            xCount+= checkinComplete == true?1:0;

            if(!Utilities.FileExistOnSdCard("process-error.mp3"))
                checkSum= "";
            boolean checkinError = dataCenter.checkRecordingFileChanged(6, checkSum);
            xCount+= checkinError == true?1:0;
            //--- 16
            if(bulaChanged)
                dataCenter.getRecordingFile(1, null, 0);
            if(insufficientChanged)
                dataCenter.getRecordingFile(2, null, 0);
            if(negativeChanged)
                dataCenter.getRecordingFile(3, null, 0);
            if(tapoutChanged)
                dataCenter.getRecordingFile(4, null, 0);
            if(checkinComplete)
                dataCenter.getRecordingFile(5,null,0);
            if(checkinError)
                dataCenter.getRecordingFile(6,null, 0);
        }
        catch (Exception ex)
        {
            //Log.d(TAG, "[GetUpdateRecording] EXCEPTION: " + ex);
        }

        return xCount;
    }

    private int GetUpdateDataDeleted()
    {
        DataCenter dataCenter = new DataCenter(getApplicationContext());
        int xCount = 0;

        try
        {
            //Log.d(TAG, "[GetUpdateDataDeleted]...");
            List<DataDeleted> updateData = dataCenter.getDataDeleted();
            if(updateData == null || updateData.size() == 0)
            {
                //Log.d(TAG, "[GetUpdateDataDeleted] No data found.");
                return xCount;
            }
            //Log.d(TAG, "[GetUpdateDataDeleted] count: " + updateData.size());
            BusinessDBHandler db = new BusinessDBHandler(getApplicationContext());
            for(DataDeleted dd: updateData)
            {
                //Log.d(TAG, String.format("[GetUpdateDataDeleted] table: %s, data: %s",
//                        dd.TableName, dd.KeyFieldValue)
//                );
                int rs = db.deleteData(dd.TableName, "isn in (" + dd.KeyFieldValue + ")");
                //Log.d(TAG, "[GetUpdateDataDeleted] result: " + rs);
                xCount += rs;
            }
        }
        catch (Exception ex)
        {
            //Log.d(TAG, "[GetUpdateDataDeleted] EXCEPTION: " + ex);
        }

        return xCount;
    }

	public boolean isNetworkAvailable()
	{
		try
		{
			Context context = this.getApplicationContext();
			ConnectivityManager connectivity = (ConnectivityManager) context
					.getSystemService(Context.CONNECTIVITY_SERVICE);
			if (connectivity != null)
			{
				NetworkInfo[] info = connectivity.getAllNetworkInfo();
				if (info != null)
				{
					for (int i = 0; i < info.length; i++)
					{
						if (info[i].getState() == NetworkInfo.State.CONNECTED)
						{
							return true;
						}
					}
				}
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
		return false;
	}

    private final class UIThreadHandler extends Handler
    {
        public static final int MSG_START_SYNC = 0x10;
        public static final int MSG_END_SYNC = 0x11;
        public static final int MSG_ERROR_NO_NETWORK = 0x12;
        public static final int MSG_SYNC_STATUS = 0x13;

        public UIThreadHandler(Looper lp)
        {
            super(lp);
        }

        @Override
        public void handleMessage(Message msg)
        {
            try
            {
                switch(msg.what)
                {
                    case UIThreadHandler.MSG_START_SYNC:
                    {
                        ShowToast("start data synchronization");
                        break;
                    }
                    case UIThreadHandler.MSG_END_SYNC:
                    {
                        ShowToast("data synchronization finished");
                        break;
                    }
                    case UIThreadHandler.MSG_ERROR_NO_NETWORK:
                    {
                        ShowToast("synchronization error: network not available");
                        break;
                    }
                    case UIThreadHandler.MSG_SYNC_STATUS:
                    {
                        Bundle data = msg.getData();
                        if(data != null)
                        {
                            ShowToast(data.getString("SYNC_STATUS_CONTENT"));
                        }
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                //Log.e(TAG, ex.getMessage());
            }
        }

        private Toast currentToast;
        private void ShowToast(String text)
        {
            if(isMyFriendActive())
            {
                currentToast = Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT);
                currentToast.show();
                //---
                Handler handler = new Handler();
                handler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        currentToast.cancel();
                    }
                },
                700);
            }
        }
    }

    private boolean isMyFriendActive()
    {
        ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
        // get the info from the currently running task
        List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);

        //Log.d(TAG, "CURRENT Activity :: "
//                + taskInfo.get(0).topActivity.getClassName());
        String activeName = taskInfo.get(0).topActivity.getClassName();
        if(activeName.contains("valuecomusa.fijibus"))
            return true;
        return false;
    }
}
